﻿// Copyright (c) 2012 - 2020 Jared Zheng <jaredz at outlook dot com>
// Buddha Framework is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
//          http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
//
// Create : 2012-12-01 version 0.1 Jared Zheng <jaredz at outlook dot com>
// Update : 2018-12-05 version 3.1.5 Jared Zheng <jaredz at outlook dot com>

#ifndef __REDIS_WRAPPER_H__
#define __REDIS_WRAPPER_H__

#pragma once

#ifdef USE_HIREDIS_LIB

#include "container.h"
#include "filelog.h"
#include "streambuf.h"
#include <hiredis.h>

#if (__PLATFORM_TARGET__ == PLATFORM_TARGET_WINDOWS)
    #include <winsock2.h>
    #pragma comment(lib, "Ws2_32.lib")
#endif

///////////////////////////////////////////////////////////////////////////////////////////////////////////
/// CRedisWrapper
class CRedisWrapper : public MObject
{
public:
    enum REDIS_PORT
    {
        REDIS_PORT_DEFAULT = 6379,
    };

    enum REDIS_TIMEOUT
    {
        REDIS_TIMEOUT_MIN     = 1000,
        REDIS_TIMEOUT_DEFAULT = 16000,
    };
public:
    CRedisWrapper(void);
    ~CRedisWrapper(void);
public:
    bool Connect(CString& strAddr, CString& strPW, Int nPort = REDIS_PORT_DEFAULT, Int nIndex = 0);
    bool Connect(PCXStr pszAddr, PCXStr pszPW = nullptr, Int nPort = REDIS_PORT_DEFAULT, Int nIndex = 0);
    void Disconnect(void);

    bool IsConnect(void);

    bool Ping(void);         // 测试连接状态
    bool ReConnect(void);    // 手工重连
    bool CheckConnect(void); // 测试连接状态, 异常重连

    Int  GetPort(void);
    CString& GetAddr(void);
    CString& GetPW(void);

    Int  GetTimeout(void);
    void SetTimeout(Int nTimeout);

    void SetFileLog(CFileLog& Log);
    CFileLog* GetFileLog(void);

    Int  GetStatus(void);
    void SetStatus(Int nStatus = 0);
    //string
    bool GetString(CString& strKey, CString& strValue);
    bool GetString(PCXStr pszKey, CString& strValue);
    bool GetString(CString& strKey, PByte pData = nullptr, size_t stSize = 0);
    bool GetString(PCXStr pszKey, PByte pData = nullptr, size_t stSize = 0);
    //bool GetString(CTArray<CString>& strKeys, CTArray<CString>& strValues);

    bool SetString(CString& strKey, CString& strValue, Int nSecond = 0);
    bool SetString(CString& strKey, PCXStr pszValue, Int nSecond = 0);
    bool SetString(PCXStr pszKey, CString& strValue, Int nSecond = 0);
    bool SetString(PCXStr pszKey, PCXStr pszValue, Int nSecond = 0);
    bool SetString(CString& strKey, PByte pData, size_t stSize, Int nSecond = 0);
    bool SetString(PCXStr pszKey, PByte pData, size_t stSize, Int nSecond = 0);
    //bool SetString(CTArray<CString>& strKeys, CTArray<CString>& strValues);
    // set
    Int  AddSetMember(CString& strKey, CString& strMember);
    Int  AddSetMember(CString& strKey, PCXStr pszMember);
    Int  AddSetMember(PCXStr pszKey, CString& strMember);
    Int  AddSetMember(PCXStr pszKey, PCXStr pszMember);
    Int  AddSetMember(CString& strKey, PByte pData, size_t stSize);
    Int  AddSetMember(PCXStr pszKey, PByte pData, size_t stSize);
    //Int  AddSetMember(CString& strKey, CTArray<CString>& strMembers);
    //Int  AddSetMember(PCXStr pszKey, CTArray<CString>& strMembers);

    Int  RemoveSetMember(CString& strKey, CString& strMember);
    Int  RemoveSetMember(CString& strKey, PCXStr pszMember);
    Int  RemoveSetMember(PCXStr pszKey, CString& strMember);
    Int  RemoveSetMember(PCXStr pszKey, PCXStr pszMember);
    Int  RemoveSetMember(CString& strKey, PByte pData, size_t stSize);
    Int  RemoveSetMember(PCXStr pszKey, PByte pData, size_t stSize);
    //Int  RemoveSetMember(CString& strKey, CTArray<CString>& strMembers);
    //Int  RemoveSetMember(PCXStr pszKey, CTArray<CString>& strMembers);

    Int  GetSetMemberCount(CString& strKey);
    Int  GetSetMemberCount(PCXStr pszKey);

    Int  GetSetMember(CString& strKey, CTArray<CString>& strMembers);
    Int  GetSetMember(PCXStr pszKey, CTArray<CString>& strMembers);
    Int  GetSetMember(CString& strKey, CTArray<CBufReadStream>& strMembers);
    Int  GetSetMember(PCXStr pszKey, CTArray<CBufReadStream>& strMembers);
    Int  GetSetMember(CString& strKey);
    Int  GetSetMember(PCXStr pszKey);

    bool RemoveKey(CString& strKey);
    bool RemoveKey(PCXStr pszKey);
    //bool RemoveKey(CTArray<CString>& strKeys);

    bool ExistKey(CString& strKey);
    bool ExistKey(PCXStr pszKey);

    Int  Publish(CString& strChannel, CString& strMsg);
    Int  Publish(CString& strChannel, PCXStr pszMsg);
    Int  Publish(PCXStr pszChannel, CString& strMsg);
    Int  Publish(PCXStr pszChannel, PCXStr pszMsg);

    // 订阅为堵塞模式
    bool Subscribe(CString& strChannel);
    bool Subscribe(PCXStr pszChannel);
    bool Subscribe(CTArray<CString>& strChannels);

    bool WaitForSubscribeMsg(CString& strChannel, CString& strMsg);

    //bool Unsubscribe(CString& strChannel);
    //bool Unsubscribe(PCXStr pszChannel);
    //bool Unsubscribe(CTArray<CString>& strChannels);
    //
    bool IsPipeCmd(void);
    bool BeginPipeCmd(void);
    Int  EndPipeCmd(bool bAll = false); // 只检测是否成功, 不需要处理返回数据
    // 按调用顺序获取返回的数据, 仍有数据返回>0, 全部完成返回0, 错误返回-1
    Int  EndPipeCmdReplay(CString& strValue);           // strValue empty if nil
    Int  EndPipeCmdReplay(PByte pData, size_t& stSize); // stSize = 0 if nil
    Int  EndPipeCmdReplay(Int& nValue);                 // nValue = 0 if nil
    Int  EndPipeCmdReplay(CTArray<CString>& strValues); // strValues size 0 if nil
private:
    bool GetString(PCXStr pszKey, size_t stKey, CString& strValue);
    bool GetString(PCXStr pszKey, size_t stKey, PByte pData, size_t stSize);
    bool SetString(PCXStr pszKey, size_t stKey, PCXStr pszValue, size_t stValue, Int nSecond);
    bool SetString(PCXStr pszKey, size_t stKey, PByte pData, size_t stSize, Int nSecond);

    Int  AddSetMember(PCXStr pszKey, size_t stKey, PCXStr pszMember, size_t stMember);
    Int  RemoveSetMember(PCXStr pszKey, size_t stKey, PCXStr pszMember, size_t stMember);
    Int  GetSetMemberCount(PCXStr pszKey, size_t stKey);
    Int  GetSetMember(PCXStr pszKey, size_t stKey, CTArray<CString>& strMembers);
    Int  GetSetMember(PCXStr pszKey, size_t stKey, CTArray<CBufReadStream>& strMembers);
    Int  GetSetMember(PCXStr pszKey, size_t stKey);

    bool RemoveKey(PCXStr pszKey, size_t stKey);
    bool ExistKey(PCXStr pszKey, size_t stKey);

    Int  Publish(PCXStr pszChannel, size_t stChannel, PCXStr pszMsg, size_t stMsg);

    bool Subscribe(PCXStr pszChannel, size_t stChannel);
    bool Subscribe(Int nSize, CTArray<CString>& strChannels);
    //
    bool Command(Int nArg, PCXStr* pszArg, size_t* pstArg);
    bool Command(CString& strValue, Int nArg, PCXStr* pszArg, size_t* pstArg);
    bool Command(PByte pData, size_t stSize, Int nArg, PCXStr* pszArg, size_t* pstArg);

    Int  CommandInt(Int nArg, PCXStr* pszArg, size_t* pstArg);
    Int  CommandInt(CTArray<CString>& strMembers, Int nArg, PCXStr* pszArg, size_t* pstArg);
    Int  CommandInt(CTArray<CBufReadStream>& strMembers, Int nArg, PCXStr* pszArg, size_t* pstArg);

    bool AppendCommand(Int nArg, PCXStr* pszArg, size_t* pstArg);
private:
    redisContext*   m_pContext;
    CFileLog*       m_pLog;
    Int             m_nStatus;
    Int             m_nPort;
    Int             m_nIndex;
    Int             m_nTimeout; // MS
    Int             m_nPipeCmd; // begin-->1, 1--->end
    CString         m_strAddr;
    CString         m_strPW;
};

///////////////////////////////////////////////////////////////////////////////////////////////////////////
#include "RedisWrapper.inl"

#endif // USE_HIREDIS_LIB

#endif // __REDIS_WRAPPER_H__
